Converting noweb markup to HTML

This copyright applies both to the noweb source and to the generated code. Thanks to Bill Trost for getting me started with an early version. «copyright notice»= # Copyright 1994 by Norman Ramsey. All rights reserved. # See file COPYRIGHT for more information. @ The [[-raw]] option brackets HTML with [[]] and [[]]; the purpose is to embed HTML in a LATEX document before converting the document with latex2html. [[braw]] and [[eraw]] hold those delimiters (or else empty strings). «*»= «copyright notice» # Don't try to understand this file! Look at icon/tohtml.nw in the noweb source!

procedure main(args) local delay, raw, where, localindex, noindex «initialization» every braw | eraw := "" delay := !args == "-delay" noindex := !args == "-noindex" localindex := (/noindex, !args == "-localindex") # produce local identifier xref? raw := !args == "-raw" if then braw := "
beginrawhtml"; eraw := "
endrawhtml" while inputline := read() do inputline ? «scan and convert» write() end «scan and convert»= «@text» «@nl» «code chunks» «@defn» «docs chunks» «@use» «@xref» «@index» «others» if ="@" then # follows last else warn_unknown(1(tab(upto(' ')|0), pos(0) | move(1))) else write(&errout, "Botched line in noweb pipeline: ", tab(0)) @ [[ecode]] is the marker used at the end of the current code chunk. If there is no cross-reference stuff at the end, we just use [[</pre>]]; otherwise we terminate whatever environment is used for the cross-reference stuff. «code chunks»= if ="@begin code " then code := 1 ; writes(braw, "<pre>"); ecode := "</pre>" else if ="@end code " then code := nil ; previscode := 1 «dump pending cross-reference info» writes(ecode, eraw) else @

We want to try to avoid emitting paragraph elements when the preceding chunk is a code chunk, as tracked by [[previscode]]. Also, if we do slip in a paragraph, we may use the LATEX style. «docs chunks»= if ="@begin docs " then if then writes(if /raw then "<p>" else "
par") previscode := &null text := 0 else @ Sometimes it happens that a document-chunk anchor is put in a document chunk that contains no text. In that case, we put in a phony anchor at the end of the chunk so we won't lose the cross-reference. «docs chunks»= if ="@end docs " then write(braw, linklabel(, "*"), eraw) lastxreflabel := &null else @ Normally, if there's a pending anchor, we put it on the first available text line. There's a bit of a fine point that crops up if the very first piece of text is quoted code. In that case we have to attach both the label for the docs anchor and the ref for the index anchor. «@text»= if ="@text " then text +:= *(line := tab(0)) if then writes((«index anchor») | escapeSpecials(line)) else if then writes((«index and docs anchor») | escapeSpecials(line)) else writes((«docs anchor») | line) else «index anchor»= 2(line ? tab(many(' '͡)); not pos(0), linkto(, escapeSpecials(line)), lastindexref := &null) @ We anchor on the first nonblank character of the line, unless that's an SGML tag, in which case we have to try to skip past. If it's an anchor tag, we give up, just inserting a * to anchor to. None of this crap would be necessary if HTML could anchor to empty text. «docs anchor»= 2(line ? tab(many(' '͡)); not pos(0), line ? braw || linklabel(, (tab(many(' '͡)) | "") || (skip_tags_and_char() | "*")) || eraw || tab(0), insert(defns_above, ), lastxreflabel := &null) @ An indexed identifier is not empty and contains no tags, so we don't have to worry. «index and docs anchor»= 2(line ? tab(many(' '͡)); not pos(0), linklabelto(lastxreflabel, lastindexref, escapeSpecials(line)), lastxreflabel := lastindexref := &null) @ Skip as many tags as possible, then skip a character, but never skip an anchor ([[<a>]]) tag, because that might mess things up big time. Argument is number of tags already skipped; if nonzero, we're willing to succeed at the end of the string. «*»= procedure skip_tags_and_char(count) local tag /count := 0 if any('&') & upto(';') then suspend(tab(upto(';')) || =";") else if any('<') then suspend ="<" || (tab(many(' '͡)) | "") || (="/" | (tag := tab(many(&letters)), map(tag) == "a", tag)) || tab(upto(">")) || =">" || skip_tags_and_char(count+1) else suspend (tab(many(' '͡)) | "") || ((move(1) | if count > 0 then "")) # succeed at end if count > 0

end @ The preceding scheme sometimes wraps an anchor around a tag. If that turns out to be bad, we could try the following alternative: «funky new docs anchor»= 2(line ? tab(many(' '͡)); not pos(0), line ? braw || scan_initial_tags() || linklabel(, (tab(many(' '͡)) | "") || (scan_past_char() | "*")) || eraw || tab(0), insert(defns_above, ), lastxreflabel := &null) «procedures to support new docs anchor»= procedure scan_initial_tags() suspend (="<" || (tab(many(' '͡)) | "") || (="/" | (tag := tab(many(&letters)), map(tag) == "a", tag)) || tab(upto(">")) || =">" || scan_initial_tags()) | "" end

procedure scan_past_char() if any('&') & upto(';') then suspend(tab(upto(';')) || =";") else suspend (tab(many(' '͡)) | "") || tab(any( '<')) end «@nl»= if ="@nl" & pos(0) then write() else «@defn»= if ="@defn " then writechunk(lastxreflabel, lastxrefref, "dfn", thischunk := tab(0), defns[thischunk] || "=") insert(defns_above, ) «clear [[lastxref*]]» defns[thischunk] := "+" else «initialization»= defns := table("") defns_above := set() # keep track of defining chunks we've seen «@use»= if ="@use " then writechunk(lastxreflabel, lastxrefref, "i", tab(0)) «clear [[lastxref*]]» else @ Writing a chunk involves creating an anchor for it. «*»= procedure writechunk(label, ref, tag, name, suffix) /suffix := "" writes(linklabelto(label, ref, sgmlwrap(tag, "&lt;" || convquotes(name) || "&gt;" || suffix))) return end @ «others»= if ="@quote" & pos(0) then quoting := 1 ; writes(braw, "<code>") else if ="@endquote" & pos(0) then quoting := nil ; writes("</code>", eraw) else «others»= if ="@file " then filename := tab(0); «clear [[lastxref*]]» else if ="@literal " then writes(tab(0)) else if ="@header html " then «write HTML header» else if ="@trailer html" & pos(0) then «write HTML trailer» else @ «write HTML header»= writes("<html><head><title>", tab(0), "</title></head><body>") «write HTML trailer»= write("</body></html>") @ «@xref»= if ="@xref " then if fun := tab(upto(' ')) then move(1); arg := tab(0) else fun := tab(0); arg := &null case fun of «cases for @xref» default : arg ? warn_unknown("xref " || fun) else «cases for @xref»= "label" : «warn if unused [[lastxreflabel]]»; lastxreflabel := arg "ref" : «warn if unused [[lastxrefref]]»; lastxrefref := arg "prevdef" : pendingprev := arg "nextdef" : pendingnext := arg "beginuses" : useitems := [] "useitem" : put(useitems, arg) "enduses" : useitemstab[thischunk] := useitems "notused" : «code-to-blockquote» writes("This code is written to a file (or else not used).<p>") «initialization»= useitemstab := table() «clear [[lastxref*]]»= every lastxreflabel | lastxrefref := &null «warn if unused [[lastxreflabel]]»= warn_unused_xref("label", ) «warn if unused [[lastxrefref]]»= warn_unused_xref("ref", ) «*»= procedure warn_unused_xref(tag, label) static warned initial warned := set() if not member(warned, tag) then insert(warned, tag) write(&errout, "Warning: internal inconsistency in noweb (not urgent)—") write(&errout, "used @xref ", tag, " ", label) return end @ «dump pending cross-reference info»= useitems := useitemstab[thischunk] if | | *> 0 then «code-to-blockquote» «write out uses with links» if *> 0 & (| ) then writes("; ") p := if *> 0 then "previous" else "Previous" n := if *> 0 then "next" else "Next" if then if then writes(linkto(pendingprev, p), " and ", linkto(pendingnext, "next"), " definitions") else writes(linkto(pendingprev, p), " definition") else if then writes(linkto(pendingnext, n), " definition") pendingprev := pendingnext := &null useitems := &null write(".<p>") «write out uses with links»= useprefix := "Used " every i := 1 to *do usedir := if member(defns_above, useitems[i]) then "above" else "below" usesuffix := if *> 1 then " (" || i || ")" else "" writes(useprefix, linkto(useitems[i], usedir || usesuffix)) useprefix := ", " @ The hack here is to put the supplementary information in a blockquote area after the code. «code-to-blockquote»= if ecode == "</pre>" then writes("</pre><blockquote>") ecode := "</blockquote>" @ The HTML back end ignores [[@xref begindefs]], [[@xref defitem]], and [[@xref enddefs]]; it uses the [[nextdef]] and [[prevdef]] links instead. «cases for @xref»= "begindefs" | "defitem" | "enddefs" : &null @ «cases for @xref»= "beginchunks" : write(braw, "<ul>") "chunkbegin" : writes("<li>"); comma := ": "; count := 0 arg ? ref := tab(upto(' ')); =" "; name := tab(0) writechunk(&null, ref, "i", name) "chunkuse" : writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", " "chunkdefn" : writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", " "chunkend" : write() "endchunks" : write("</ul>", eraw) «cases for @index»= "beginindex" : write(braw, "<ul>") "entrybegin" : writes("<li>"); comma := ": "; count := 0 arg ? ref := tab(upto(' ')); =" "; name := tab(0) writes(linklabelto("NWI-" || escapeSpecials(name), ref, name)) "entryuse" : writes(comma, linkto(arg, "U" || (count +:= 1))); comma := ", " "entrydefn" : writes(comma, linkto(arg, "D" || (count +:= 1))); comma := ", " "entryend" : write() "endindex" : write("</ul>", eraw) «@index»= if ="@index " then if /noindex then if fun := tab(upto(' ')) then move(1); arg := tab(0) else fun := tab(0); arg := &null case fun of «cases for @index» default : arg ? warn_unknown("index " || fun) # don't get any warnings if not doing indexing else @ The local identifier cross-reference doesn't show each use; it just shows the identifiers that are defined, with links to the full index. «cases for @index»= "use" : lastindexref := lastxrefref; lastxrefref := &null "defn" : «clear [[lastxref*]]» "localdefn" : «clear [[lastxref*]]» "nl" : &null # do nothing – destroys line numbering "begindefs" : if then «code-to-blockquote»; writes("Defines"); comma := " " "isused" : &null "defitem" : if then writes(comma, linkto("NWI-" || escapeSpecials(arg), sgmlwrap("code", escapeSpecials(arg)))) comma := ", " "enddefs" : if then write(" (links are to index).<p>") "beginuses" | "isdefined" | "useitem" | "enduses" : &null # use local links @


Subsections